import { Light } from './lights/Light'
import { PointLight } from './lights/PointLight'
import { Color } from './math/Color'
import { BufferGeometry } from './core/BufferGeometry'
import { Geometry } from './core/Geometry'

//
// Custom QUnit assertions.
//

// import {SmartComparer} from './SmartComparer'
// import {ObjectLoader} from '../../../src/loaders/ObjectLoader'

// QUnit.assert.success = function (message) {
// 	// Equivalent to assert( true, message );
// 	this.pushResult({
// 		result: true,
// 		actual: undefined,
// 		expected: undefined,
// 		message: message,
// 	})
// }

// QUnit.assert.fail = function (message) {
// 	// Equivalent to assert( false, message );
// 	this.pushResult({
// 		result: false,
// 		actual: undefined,
// 		expected: undefined,
// 		message: message,
// 	})
// }

// QUnit.assert.numEqual = function (actual, expected, message) {
// 	var diff = Mathf.abs(actual - expected)
// 	message = message || actual + ' should be equal to ' + expected
// 	this.pushResult({
// 		result: diff < 0.1,
// 		actual: actual,
// 		expected: expected,
// 		message: message,
// 	})
// }

// QUnit.assert.equalKey = function (obj, ref, key) {
// 	var actual = obj[key]
// 	var expected = ref[key]
// 	var message = actual + ' should be equal to ' + expected + ' for key "' + key + '"'
// 	this.pushResult({
// 		result: actual == expected,
// 		actual: actual,
// 		expected: expected,
// 		message: message,
// 	})
// }

// QUnit.assert.smartEqual = function (actual, expected, message) {
// 	var cmp = new SmartComparer()

// 	var same = cmp.areEqual(actual, expected)
// 	var msg = cmp.getDiagnostic() || message

// 	this.pushResult({
// 		result: same,
// 		actual: actual,
// 		expected: expected,
// 		message: msg,
// 	})
// }

//
//	GEOMETRY TEST HELPERS
//

// TODO
// function checkGeometryClone(geom: BufferGeometry) {
// 	// Clone
// 	var copy = geom.clone()
// 	expect(copy.uuid).not.toStrictEqual( geom.uuid, 'clone uuid should differ from original')
// 	expect(copy.id).not.toStrictEqual(geom.id, 'clone id should differ from original')

// 	var excludedProperties = ['parameters', 'widthSegments', 'heightSegments', 'depthSegments']

// 	var differingProp = getDifferingProp(geom, copy, excludedProperties)
// 	QUnit.assert.ok(differingProp === undefined, 'properties are equal')

// 	differingProp = getDifferingProp(copy, geom, excludedProperties)
// 	QUnit.assert.ok(differingProp === undefined, 'properties are equal')

// 	// TODO? json round trip with clone
// 	// checkGeometryJsonRoundtrip(copy)
// }

// function getDifferingProp(geometryA, geometryB, excludedProperties) {
// 	excludedProperties = excludedProperties || []

// 	var geometryKeys = Object.keys(geometryA)
// 	var cloneKeys = Object.keys(geometryB)

// 	var differingProp = undefined

// 	for (var i = 0, l = geometryKeys.length; i < l; i++) {
// 		var key = geometryKeys[i]

// 		if (excludedProperties.indexOf(key) >= 0) continue

// 		if (cloneKeys.indexOf(key) < 0) {
// 			differingProp = key
// 			break
// 		}
// 	}

// 	return differingProp
// }

// // Compare json file with its source geometry.
// function checkGeometryJsonWriting(geom, json) {
// 	QUnit.assert.equal(json.metadata.version, '4.5', 'check metadata version')
// 	QUnit.assert.equalKey(geom, json, 'type')
// 	QUnit.assert.equalKey(geom, json, 'uuid')
// 	QUnit.assert.equal(json.id, undefined, 'should not persist id')

// 	var params = geom.parameters
// 	if (!params) {
// 		return
// 	}

// 	// All parameters from geometry should be persisted.
// 	var keys = Object.keys(params)
// 	for (var i = 0, l = keys.length; i < l; i++) {
// 		QUnit.assert.equalKey(params, json, keys[i])
// 	}

// 	// All parameters from json should be transfered to the geometry.
// 	// json is flat. Ignore first level json properties that are not parameters.
// 	var notParameters = ['metadata', 'uuid', 'type']
// 	var keys = Object.keys(json)
// 	for (var i = 0, l = keys.length; i < l; i++) {
// 		var key = keys[i]
// 		if (notParameters.indexOf(key) === -1) QUnit.assert.equalKey(params, json, key)
// 	}
// }

// // Check parsing and reconstruction of json geometry
// function checkGeometryJsonReading(json, geom) {
// 	var wrap = [json]

// 	var loader = new ObjectLoader()
// 	var output = loader.parseGeometries(wrap)

// 	QUnit.assert.ok(output[geom.uuid], 'geometry matching source uuid not in output')
// 	// QUnit.assert.smartEqual( output[ geom.uuid ], geom, 'Reconstruct geometry from ObjectLoader' );

// 	var differing = getDifferingProp(output[geom.uuid], geom, ['bones'])
// 	if (differing) console.log(differing)

// 	var excludedProperties = ['bones']

// 	var differingProp = getDifferingProp(output[geom.uuid], geom, excludedProperties)
// 	QUnit.assert.ok(differingProp === undefined, 'properties are equal')

// 	differingProp = getDifferingProp(geom, output[geom.uuid], excludedProperties)
// 	QUnit.assert.ok(differingProp === undefined, 'properties are equal')
// }

// // Verify geom -> json -> geom
// function checkGeometryJsonRoundtrip(geom) {
// 	var json = geom.toJSON()
// 	checkGeometryJsonWriting(geom, json)
// 	checkGeometryJsonReading(json, geom)
// }

// Look for undefined and NaN values in numerical fieds.
function checkFinite<G>(geom: G): void {
	let allVerticesAreFinite = true

	if (geom instanceof Geometry) {
		const vertices = geom.vertices

		for (let i = 0, l = vertices.length; i < l; i++) {
			const v = geom.vertices[i]

			if (!(isFinite(v.x) || isFinite(v.y) || isFinite(v.z))) {
				allVerticesAreFinite = false
				break
			}
		}
	}

	// TODO BufferGeometry, normal, etc.
	todo('runStdGeometryTests for BufferGeometry too')

	expect(allVerticesAreFinite).toBe(true, 'contains only finite coordinates')
}

// Run common geometry tests.
export function runStdGeometryTests<G>(geometries: G[]): void {
	for (let i = 0, l = geometries.length; i < l; i++) {
		const geom = geometries[i]

		checkFinite(geom)

		// TODO Clone
		// checkGeometryClone(geom)

		// TODO? json round trip
		// checkGeometryJsonRoundtrip(geom)
	}
}

// //
// //	LIGHT TEST HELPERS
// //

// // Run common light tests.
// export function runStdLightTests(lights: Light[]) {
// 	for (var i = 0, l = lights.length; i < l; i++) {
// 		var light = lights[i]

// 		// copy and clone
// 		checkLightCopyClone(light)

// 		// THREE.Light doesn't get parsed by ObjectLoader as it's only
// 		// used as an abstract base class - so we skip the JSON tests
// 		if (light.type !== 'Light') {
// 			// json round trip
// 			checkLightJsonRoundtrip(assert, light)
// 		}
// 	}
// }

// function getLightClass(light: Light): typeof Light {
// 	if (light instanceof PointLight) return PointLight
// 	if (light instanceof Light) return Light
// 	abort('not a light class')
// }

// function checkLightCopyClone(light: Light) {
// 	// copy
// 	const Class = getLightClass(light)
// 	var newLight = new Class(new Color(0xc0 / 0xff, 0xff / 0xff, 0xee / 0xff))
// 	newLight.copy(light)

// 	expect(newLight.uuid).not.toBe(light.uuid, "Copied light's UUID differs from original")
// 	expect(newLight.id).not.toBe(light.id, "Copied light's id differs from original")
// 	expect(newLight).toStrictEqual(light, 'Copied light is equal to original')

// 	// real copy?
// 	newLight.color.setHex(0xc0ffee)
// 	QUnit.assert.notStrictEqual(
// 		newLight.color.getHex(),
// 		light.color.getHex(),
// 		'Copied light is independent from original'
// 	)

// 	// Clone
// 	var clone = light.clone() // better get a new var
// 	QUnit.assert.notEqual(clone.uuid, light.uuid, "Cloned light's UUID differs from original")
// 	QUnit.assert.notEqual(clone.id, light.id, "Clone light's id differs from original")
// 	QUnit.assert.smartEqual(clone, light, 'Clone light is equal to original')

// 	// real clone?
// 	clone.color.setHex(0xc0ffee)
// 	QUnit.assert.notStrictEqual(clone.color.getHex(), light.color.getHex(), 'Clone light is independent from original')

// 	if (light.type !== 'Light') {
// 		// json round trip with clone
// 		checkLightJsonRoundtrip(assert, clone)
// 	}
// }

// // Compare json file with its source Light.
// function checkLightJsonWriting(assert, light, json) {
// 	assert.equal(json.metadata.version, '4.5', 'check metadata version')

// 	var object = json.object
// 	assert.equalKey(light, object, 'type')
// 	assert.equalKey(light, object, 'uuid')
// 	assert.equal(object.id, undefined, 'should not persist id')
// }

// // Check parsing and reconstruction of json Light
// function checkLightJsonReading(assert, json, light) {
// 	var loader = new ObjectLoader()
// 	var outputLight = loader.parse(json)

// 	assert.smartEqual(outputLight, light, 'Reconstruct Light from ObjectLoader')
// }

// // Verify light -> json -> light
// function checkLightJsonRoundtrip(assert, light) {
// 	var json = light.toJSON()
// 	checkLightJsonWriting(assert, light, json)
// 	checkLightJsonReading(assert, json, light)
// }
